<template>
  <view class="mxp-drop-down-single" :class="[cusClass]" :style="[cusStyle]">
    <!-- 顶部遮罩 -->
    <view v-if="data.show" :style="data.topMaskStyle" @click="preventClick" @touchmove.stop.passive="preventClick"></view>
    <!-- 标题部分 -->
    <view :id="id" class="drop-down-menu" :style="[{ background }, titleStyle]">
      <slot name="menu" :newMenuList="newMenuList" :currentIndex="data.currentIndex">
        <view
          v-for="(item, index) in newMenuList"
          :key="index"
          :style="{ width: newTitleItemWidth }"
          class="drop-down-menu-item"
          @click.stop.prevent="titleItemClick(item, index)"
          @touchmove.stop.passive="preventClick"
        >
          <text
            class="text"
            :style="{
              color: (!item.isCheckbox && item.defaultValue !== undefined) || (item.isCheckbox && item.defaultValue) ? activeColor : inactiveColor,
              fontSize: titleFontSize,
            }"
          >
            {{ item.title }}
          </text>
          <view
            v-if="!item.isCheckbox"
            key="view-1"
            class="arrows-box"
            :style="{ transform: data.currentIndex === index && data.arrowsState ? 'rotate(180deg)' : 'rotate(0deg)' }"
          >
            <mxp-icon name="xiasanjiao" :color="item.defaultValue !== undefined ? activeColor : inactiveColor" size="12px"></mxp-icon>
          </view>
          <view v-else key="view-2" style="width: 14px; margin-left: 4px; overflow: hidden">
            <view
              class="dui-box"
              :style="{ background: item.defaultValue ? activeColor : 'transparent', border: `1px solid ${item.defaultValue ? activeColor : inactiveColor}` }"
            >
              <mxp-icon name="dui" :color="item.defaultValue ? '#ffffff' : inactiveColor" size="12px"></mxp-icon>
            </view>
          </view>
        </view>
      </slot>
    </view>
    <!-- 筛选部分 -->
    <view
      v-if="data.showPopup"
      key="view-2"
      :style="[data.maskStyle, { transitionDuration: `${duration}ms`, opacity }]"
      class="mask"
      @click="onMaskClose"
      @touchmove.stop.passive="preventClick"
    >
      <view
        :style="[{ zIndex: zIndex! + 1, transform, transitionDuration: `${duration}ms`, opacity }]"
        class="popup"
        @click="preventClick"
        @touchmove.stop.passive="preventClick"
      >
        <scroll-view :scrollY="true" :style="{ background, maxHeight: scrollMaxHeight }" @click.stop.prevent="preventClick">
          <slot name="child" :currMenuInfo="currMenuInfo" :currentIndex="data.currentIndex">
            <template v-if="currMenuInfo && currMenuInfo.child.length > 0 && !currMenuInfo.isCheckbox">
              <view
                v-for="(item, index) in currMenuInfo.child"
                :key="index"
                class="drop-down-item"
                :style="{ height: itemHeight }"
                @click.stop.passive="subItemClick(item, index)"
              >
                <text class="item-text" :style="{ color: currMenuInfo.defaultValue === item.value ? activeColor : inactiveColor, fontSize: itemFontSize }">
                  {{ item.label }}
                </text>
                <mxp-icon v-if="currMenuInfo.defaultValue === item.value" name="dui" :color="activeColor" :size="itemFontSize"></mxp-icon>
              </view>
            </template>
            <template v-else-if="!currMenuInfo || (currMenuInfo && currMenuInfo.child.length <= 0)">
              <view style="display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 200px">
                <image src="../mxp-tool/static/images/global-state/not-data.svg" style="width: 120px; height: 120px" mode="scaleToFill"></image>
                <view>
                  <text style="font-size: 14px" :style="{ color: inactiveColor }">暂无可选数据</text>
                </view>
              </view>
            </template>
          </slot>
        </scroll-view>
      </view>
    </view>
  </view>
</template>

<script lang="ts" setup>
  import { publicStyeProps } from '../mxp-tool/utils/public-props';
  import { getDomSize, getXPNumber } from '../mxp-tool/utils/dom';
  import MxpIcon from '../mxp-icon/mxp-icon.vue';
  import type { $AllColorStr, $SizeUnitStr } from '@/mxp-ui/mxp-tool/types/interface';
  import { type CSSProperties, type PropType, computed, getCurrentInstance, nextTick, ref, shallowReactive } from 'vue';
  import { isObject } from '../mxp-tool/utils/verify';
  import { getOnlyID } from '../mxp-tool/utils/common';
  import mxpGetSysInfoStore from '../mxp-tool/store/mxp-store-sys-info';

  export type $BaseItem = {
    /** 默认值 */
    defaultValue?: unknown | undefined;
    /** 是否为Checkbox按钮 */
    isCheckbox?: boolean;
    /** 是否可以取消选中 (为 false 时 选中后再次点击 可以取消选中) */
    uncheck?: boolean;
  };

  export type $DropDownSingleListItem = $BaseItem & Record<number | string, unknown>;

  export type $TrimChildItem = { label: string; value: unknown; item: unknown };

  export type $TrimDropDownSingleListItem = Required<$BaseItem> & {
    /** 传给后端需要的key  必须保证唯一 */
    key: number | string;
    /** 最终显示的 lablel */
    title: number | string;
    /** 最终的 child 列表 */
    child: $TrimChildItem[];
  };

  export type $DropDownSingleSelectedDetails = Record<number | string, { index: number; value: unknown; item: unknown }>;

  export type $DropDownSingleResult = {
    /** 选中的值 */
    selected: Record<keyof $DropDownSingleListItem, unknown>;
    /** 选中的详情 */
    selectedDetails: $DropDownSingleSelectedDetails;
  };
  export type $DropDownSingleChange = $DropDownSingleResult & {
    /** 当前改变的 title 数组的下标 */
    titleIndex: number;
    /** 当前改变的 child 数组的下标 */
    index: number;
    /** 当前改变的 child 数组的 值 */
    value: unknown;
  };

  defineOptions({
    name: 'mxp-drop-down-single',
  });
  const props = defineProps({
    /**
     * 菜单
     * @example  [{
     *              label: '地区', //  可通过 传入 props 中的 titleLabelKey 字段修改为自定义的 key
     *              key: 'areaId', // 传给后端需要的key 必须保证唯一   可通过 传入 props 中的 onlyKey 字段修改为自定义的 key
     *              defaultValue: undefined, // 未选中时的默认值(可不写此字段或传undefined)用于为选中时返回给后端一个默认值
     *              uncheck: undefined, // 是否可以取消选中 (为 false 时 选中后再次点击 可以取消选中)
     *              isCheckbox: true, // // 是否为Checkbox按钮
     *              child: [ // 可通过 传入 props 中的 childKey 字段修改为自定义的 key
     *                  {
     *                      label: '全部地区',  // 可通过 传入 props 中的 childLabelKey 字段修改为自定义的 key
     *                      value: '121'   // 可通过 传入 props 中的 childValueKey 字段修改为自定义的 key
     *                  },
     *                  {
     *                      label: '上海',  // 可通过 传入 props 中的 childLabelKey 字段修改为自定义的 key
     *                      value: '332'  // 可通过 传入 props 中的 childValueKey 字段修改为自定义的 key
     *                   }
     *              ],
     *           }]
     */
    list: { type: Array as PropType<$DropDownSingleListItem[]>, default: () => [] },
    /** 默认值优先级高于在 list 里面设置的默认值 */
    modelValue: { type: Object as PropType<Record<keyof $DropDownSingleListItem, unknown>>, default: undefined },
    /** 需要传给后端的key 必须保证唯一, 默认为: 'key' */
    onlyKey: { type: String, default: 'key' },
    /** 标题的显示字段的 key,不传取 childLabelKey 的值，如果都没有则为 'label' 即：props.titleLabelKey || props.childLabelKey || 'label' */
    titleLabelKey: { type: String, default: undefined },
    /** 选项列表的字段, 默认为: 'child' */
    childKey: { type: String, default: 'child' },
    /** 选中的值的字段的key, 默认为: 'value' */
    childValueKey: { type: String, default: 'value' },
    /** 显示字段的 key,不传取 titleLabelKey 的值，如果都没有则为'label' 即：props.childLabelKey || props.titleLabelKey || 'label' */
    childLabelKey: { type: String, default: undefined },
    /** 是否只有选中结果改变时才触发 submit 事件 */
    isChangeSubmit: { type: Boolean, default: true },
    /** 是否可以点击遮罩关闭 */
    maskClose: { type: Boolean, default: true },
    /** zIndex层级默认为 100 */
    zIndex: { type: Number, default: 100 },
    /** 标题的文字大小 */
    titleFontSize: { type: String as PropType<$SizeUnitStr>, default: `14px` },
    /** 筛选 列表的每一项 的高度 */
    itemHeight: { type: String as PropType<$SizeUnitStr>, default: `44px` },
    /** 筛选 列表的每一项 的文字大小 */
    itemFontSize: { type: String as PropType<$SizeUnitStr>, default: `14px` },
    /** item数量超过多少开始滚动 */
    maxItemCount: { type: Number, default: 6 },
    /** 筛选菜单的背景色 */
    background: { type: String as PropType<$AllColorStr>, default: '#ffffff' },
    /** 未选中颜色 */
    inactiveColor: { type: String as PropType<$AllColorStr>, default: '#666' },
    /** 选中颜色颜色 */
    activeColor: { type: String as PropType<$AllColorStr>, default: '#0377fc' },
    /** 动画持续时间 */
    duration: { type: Number, default: 300 },
    /** 标题了的宽度，默认五个以内就平均分配，五个以上每项宽度为20% */
    titleItemWidth: { type: String as PropType<$SizeUnitStr | `${number}%`>, default: undefined },
    /** 标题的样式,可手动调整标题的高度边距等等 */
    titleStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
    ...publicStyeProps,
  });

  defineSlots<{
    /** 自定义菜单 */
    menu?: () => void;
    /** 自定义筛选部分 */
    child?: (data: { currMenuInfo?: $TrimDropDownSingleListItem; currentIndex: number }) => void;
  }>();

  const emits = defineEmits<{
    (e: 'submit', data: $DropDownSingleResult): void;
    (e: 'change', data: $DropDownSingleChange): void;
    (e: 'titleClick', data: { item: $TrimDropDownSingleListItem; index: number }): void;
    (e: 'itemClick', data: { titleIndex: number; item: any; index: number }): void;
  }>();

  const instance = getCurrentInstance();

  const id = getOnlyID('mxp-drop-down-single');
  const opacity = ref(0);
  const transform = ref('translateY(-100%)');
  const animationTimeout = ref<NodeJS.Timeout | undefined>(undefined);
  const data = shallowReactive({
    menuStyle: {} as CSSProperties,
    topMaskStyle: { position: 'fixed', zIndex: props.zIndex } as CSSProperties,
    maskStyle: { position: 'fixed', zIndex: props.zIndex } as CSSProperties,
    popupHeight: '0px' as $SizeUnitStr,
    maskHeight: '0px' as $SizeUnitStr,
    currentIndex: 0,
    showPopup: false, // 动画结束后销毁
    show: false, // 显示或隐藏
    arrowsState: false,
  });

  /** 菜单 */
  const newMenuList = ref<$TrimDropDownSingleListItem[]>(updateList(props.list));
  /** 滚动条高度 */
  const scrollMaxHeight = computed(() => props.maxItemCount * getXPNumber(props.itemHeight));
  /** 当前展示的菜单 */
  const currMenuInfo = computed<$TrimDropDownSingleListItem | undefined>(() => newMenuList.value[data.currentIndex]);
  /** 每个标题的宽度 */
  const newTitleItemWidth = computed<$SizeUnitStr | `${number}%`>(() => {
    return props.titleItemWidth ? props.titleItemWidth : newMenuList.value.length <= 5 ? `${100 / newMenuList.value.length}%` : '20%';
  });

  /**
   * 显示
   */
  async function show() {
    const rect = await getDomSize(`#${id}`, instance);
    const sys = mxpGetSysInfoStore().getSysInfo();
    data.menuStyle = {
      position: 'fixed',
      top: `${rect.top!}px`,
      left: `${rect.left!}px`,
      zIndex: props.zIndex + 1,
      width: `${rect.width}px`,
      height: `${rect.height!}px`,
    };
    data.topMaskStyle = {
      position: 'fixed',
      top: `${sys.value.windowTop}px`,
      left: 0,
      zIndex: props.zIndex,
      width: `${sys.value.windowWidth}px`,
      height: `${rect.top!}px`,
    };

    data.maskStyle = {
      position: 'fixed',
      bottom: `0px`,
      left: 0,
      zIndex: props.zIndex,
      width: `${sys.value.windowWidth}px`,
      height: `${sys.value.windowHeight - rect.top! - rect.height!}px`,
      backgroundColor: `rgba(0, 0, 0,0.3)`,
    };
    animationTimeout.value && clearTimeout(animationTimeout.value);
    opacity.value = 0;
    transform.value = `translateY(-100%)`;
    data.show = true;
    data.showPopup = true;

    nextTick(() => {
      setTimeout(() => {
        data.arrowsState = true;
        opacity.value = 1;
        transform.value = `translateY(0)`;
      }, 0);
    });
  }

  /**
   * 隐藏
   */
  function hide() {
    animationTimeout.value && clearTimeout(animationTimeout.value);
    opacity.value = 0;
    transform.value = `translateY(-100%)`;
    data.arrowsState = false;
    animationTimeout.value = setTimeout(() => {
      animationTimeout.value = undefined;
      data.show = false;
      data.showPopup = false;
    }, props.duration);
  }

  /**
   * 更新数据
   */
  function updateList(list: $DropDownSingleListItem[]): $TrimDropDownSingleListItem[] {
    const newList: Record<string, unknown>[] = JSON.parse(JSON.stringify(list));
    const titleLabelKey = props.titleLabelKey || props.childLabelKey || 'label';
    const childLabelKey = props.childLabelKey || titleLabelKey || 'label';
    return newList.map((item: $DropDownSingleListItem, index) => {
      const child = (props.childKey in item && Array.isArray(item[props.childKey]) ? item[props.childKey] : []) as unknown[];
      // 把非对象数组处理成对象数组
      const childList: $TrimChildItem[] = child.map((_item, index2) => {
        if (isObject(_item)) {
          const value = _item[props.childValueKey] || _item[childLabelKey] || `value_${index2}`;
          return { label: String(_item[childLabelKey] || `label_${index2}`), value, item: _item };
        } else {
          return { label: String(_item), value: _item, item: _item };
        }
      });
      // 得到唯一 id
      const onlyKey: number | string = (props.onlyKey in item ? (item[props.onlyKey] as number | string) : null) ?? `value${index}`;
      // modelValue 中是否有选中的
      let defaultValue: unknown | undefined = props.modelValue && onlyKey in props.modelValue ? props.modelValue![onlyKey] : undefined;
      defaultValue = defaultValue ? defaultValue : 'defaultValue' in item ? item.defaultValue : undefined;

      const active = childList.find((item2) => item2.value === defaultValue);
      const label = active !== undefined ? active?.label : titleLabelKey in item ? item[titleLabelKey] : `lable${index}`;
      const newItem: $TrimDropDownSingleListItem = {
        key: onlyKey,
        title: String(label),
        uncheck: Boolean(item.uncheck ?? true),
        defaultValue,
        isCheckbox: item.isCheckbox ?? false,
        child: childList,
      };

      return newItem;
    });
  }

  /**
   * 阻止传递
   */
  function preventClick() {
    // 阻止传递
  }

  /**
   * 点击遮罩
   */
  function onMaskClose() {
    props.maskClose && hide();
  }

  /**
   * 点击标题
   */
  function titleItemClick(item: $TrimDropDownSingleListItem, index: number) {
    emits('titleClick', { item, index });
    const isCheckbox = item.isCheckbox ?? false;
    if (data.currentIndex === index) {
      // 点击当前项
      data.showPopup || isCheckbox ? hide() : show();
    } else {
      (!data.showPopup && !isCheckbox) || (data.showPopup && !isCheckbox) ? show() : hide();
      data.currentIndex = index;
    }
    if (item.isCheckbox) {
      item.defaultValue = !item.defaultValue;
      const selected = getResult();
      emits('submit', selected);
      emits('change', { ...selected, titleIndex: data.currentIndex, index: -1, value: item.defaultValue });
    }
  }

  /**
   * 选中或者取消选中
   */
  function subItemClick(item: $TrimChildItem, index: number) {
    emits('itemClick', { titleIndex: data.currentIndex, item, index });
    const oldValue = newMenuList.value[data.currentIndex]!.defaultValue;

    if (newMenuList.value[data.currentIndex]) {
      if (!newMenuList.value[data.currentIndex]!.uncheck && oldValue === item.value) {
        // 如果不是必选且之前和现在点击的是同一个就取消选中
        newMenuList.value[data.currentIndex]!.defaultValue = undefined;
        newMenuList.value[data.currentIndex]!.title = String(newMenuList.value[data.currentIndex]!.title || '');
      } else {
        newMenuList.value[data.currentIndex]!.defaultValue = item.value;
        newMenuList.value[data.currentIndex]!.title = String(item.label || '');
      }
      const newValue = newMenuList.value[data.currentIndex]!.defaultValue;
      const selectedInfo = getResult();
      emits('change', { ...selectedInfo, titleIndex: data.currentIndex, index, value: newValue });
      if (oldValue !== newValue || !props.isChangeSubmit) {
        emits('submit', selectedInfo);
      }
      hide();
    }
  }

  /** 得到选中的值 */
  function getResult(): $DropDownSingleResult {
    const selected: $DropDownSingleResult['selected'] = {};
    const selectedDetails: $DropDownSingleResult['selectedDetails'] = {};
    newMenuList.value.forEach((item) => {
      if (item.defaultValue !== undefined) {
        const key = item.key || '';
        selected[key] = item.defaultValue;
        const index = item.child.findIndex((item2) => item2.value === selected[key]);
        selectedDetails[key] = { index, value: selected[key], item: index >= 0 ? item.child[index] : undefined };
      }
    });
    return { selected, selectedDetails };
  }

  defineExpose({
    /** 显示 */
    show,
    /** 隐藏 */
    hide,
    /** 更新数据 */
    updateList,
    /** 得到选中的值 */
    getResult,
  });
</script>

<style scoped lang="scss">
  .mask {
    overflow: hidden;
    opacity: 0;
    transition-timing-function: ease-in-out;
    transition-duration: 0.3s;
    transition-property: opacity;

    .popup {
      transition-timing-function: ease-in-out;
      transition-duration: 0.3s;
      transition-property: transform, opacity;
      transform: translateY(-100%);
    }
  }

  .drop-down-menu {
    display: flex;
    flex: 1;
    flex-flow: row wrap;
    align-items: center;
    justify-content: space-around;
    padding: 6px 0;

    .drop-down-menu-item {
      box-sizing: border-box;
      display: flex;
      flex-flow: row nowrap;
      align-items: center;
      justify-content: center;
      padding: 0 6px;

      .text {
        display: block;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .arrows-box {
        width: 12px;
        height: 12px;
        margin-left: 2px;
        overflow: hidden;
        transition: transform 0.3s;
        transform-origin: center center;
      }

      .dui-box {
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: center;
        width: 24px;
        height: 24px;
        overflow: hidden;
        border-radius: 50%;
        transform: scale(0.5);
        transform-origin: left center;
      }
    }
  }

  .drop-down-item {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    padding: 0 15px;
    font-size: 14px;
    color: #111111;
    border-top: 1px solid #d6d7d9;

    .item-text {
      flex: 1;
    }
  }
</style>
